﻿using System;
using System.Timers;
using System.Drawing;
using System.Windows.Forms;

using OpenTK;
using OpenTK.Input;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;

using GLSL;

using MosCraft.Utilities;
using MosCraft.Utilities.Input;
using MosCraft.Graphics;

namespace MosCraft
{
    public class MosCraft : GameWindow
    {
        #region properties
        internal const bool DEBUG = true;
        internal const string VERSION = "MosCraft Alpha (0.1.7)";
        internal const string FOLDERNAME = "MosCraft";
        internal static string USERSOTRAGE = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
        internal const int RENDERRANGE = 240;
        internal static int clientWidth = 900;
        internal static int clientHight = 500;

        public FontPrinter font;
        public World world;
        public Entity Player;

        public SkyBox skyBox;
        public GLSLShader SolidShader;
        public GLSLShader BlendShader;

        public bool gamePaused = false;
        #endregion

        public MosCraft(GraphicsMode mode)
            : base(clientWidth, clientHight, mode, VERSION)
        {
            VSync = VSyncMode.Adaptive;
            Keyboard.KeyDown += KeyDown;
            Mouse.ButtonDown += MouseButtonDown;
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            Settings.initialize(USERSOTRAGE + @"\" + FOLDERNAME);
           
            #region Start-up Checks

            string openGLVersion = GL.GetString(StringName.Version);
            
            bool isFirstRun;
            Settings.Config.GetField("Stats", "FirstRun", out isFirstRun);

            if (DEBUG)
            {
                Console.WriteLine(VERSION + " Debug Log");
                Console.WriteLine("OS: " + System.Environment.OSVersion.ToString());
                Console.WriteLine("Renderer: " + GL.GetString(StringName.Renderer));
                Console.WriteLine("Vendor: " + GL.GetString(StringName.Vendor));
                Console.WriteLine("Version: " + GL.GetString(StringName.Version));
                Console.WriteLine(".Net: " + Environment.Version);
                Console.WriteLine("LocalData: " + USERSOTRAGE + @"\" + FOLDERNAME);

                string ext = GL.GetString(StringName.Extensions);

                bool isDisableShaders, useFBO;

                Settings.Config.GetField("Graphics", "DisableShaders", out isDisableShaders);
                Settings.Config.GetField("Graphics", "useFBO", out useFBO);

                if (ext.Contains("vertex_shader") && ext.Contains("fragment_shader"))
                {
                    if (isDisableShaders)
                    {
                        Console.WriteLine("Shaders supported but disabled.");
                        //GLSLShader.IsSupported = false;
                    }
                    else
                    {
                        Console.WriteLine("Shaders supported.");
                        //GLSLShader.IsSupported = true;
                    }
                }
                else
                {
                    Console.WriteLine("Shaders not supported.");
                    //GLSLShader.IsSupported = false;
                }

                if (ext.Contains("EXT_framebuffer_object"))
                {
                    if (useFBO)
                    {
                        Console.WriteLine("FBOs supported but disabled.");
                        //FBO.IsSupported = false;
                    }
                    else
                    {
                        Console.WriteLine("FBOs supported.");
                        //FBO.IsSupported = true;
                    }
                }
                else
                {
                    Console.WriteLine("FBOs not supported.");
                    //FBO.IsSupported = false;
                }
            }

            if (openGLVersion[0] <= 1 && openGLVersion[2] < 5)
            {
                MessageBox.Show("You need at least OpenGL 1.5 to run this program. Please update your drivers.\n" +
                                "You have " + openGLVersion.Substring(0, 3) + " installed."
                                , "OpenGL Error",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Exclamation);
                this.Exit();
            }


            if (!isFirstRun)
            {
                DialogResult submitData = MessageBox.Show("Would like to submit anonymous data to us?\n" +
                                                            "The Data submitted is you machine specs and average FPS.",
                        "Anonymous Data Submission",
                        MessageBoxButtons.YesNo,
                        MessageBoxIcon.Question,
                        MessageBoxDefaultButton.Button2);

                if (submitData == DialogResult.Yes)
                {
                    Settings.Config.SetField("Stats", "CollectData", true);
                }
                else
                {
                    Settings.Config.SetField("Stats", "CollectData", false);
                }

                Settings.Config.SetField("Stats", "FirstRun", true);
            }
            #endregion

            MouseHelper.Initialize(this);
            MouseHelper.SetSensitivity(0.001f);
            MouseHelper.GrabMouse();

            Texture.initialize("font.png");
            Texture.initialize("terrain.png");
            Texture.initialize("clouds.png");
            Texture.initialize("sun.png");
            Texture.initialize("moon.png");

            font = new FontPrinter();
            world = new World("test", this, DateTime.Now.Millisecond);

            Player = new Entity(world);
            Player.setPosition(-1, 80, 8);

            OpenGLHelper.initOpenGL();
            GL.ClearColor(Color4.Black);

            skyBox = new SkyBox(world, Player);

            if (DEBUG) Console.WriteLine("OnLoad Complete!");
        }

        protected override void OnUnload(EventArgs e)
        {
            base.OnUnload(e);
        }

        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            Settings.save();
            base.OnClosing(e);
        }

        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            KeyboardHelper.Update();
            MouseHelper.Update();
            
            base.OnUpdateFrame(e); 
            world.Update();
            Player.onUpdate(e.Time);
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            clientHight = Height;
            clientWidth = Width;
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            Color4 FogColor = world.getFogColor(1.0f);
            GL.ClearColor(FogColor);
            
            //Set to 3D projection
            OpenGLHelper.initSceneRendering();
            Player.getFirstPersonCamera().lookThrough(256.0f);

            //Render the sun, moon stars clouds ect
            //All relative to the entity postion
            skyBox.render(1.0f);

            //Render the world!
            world.Render();

            //Render the players block selector
            Player.renderRayCast();

            //Set to Orth
            #region 2D
             OpenGLHelper.initOverlayRendering();

            Player.renderGUI();

            GL.Color4(Color4.White);
            GL.Enable(EnableCap.Texture2D);
            GLSL.Texture.getTexture("font.png").bind();

            bool showDebug;

            Settings.Config.GetField("Controls", "ShowDebug", out showDebug);

            if (showDebug)
            {
                //Left
                font.Write(4, 4, "FPS:" + string.Format("{0:0.##}", 1.0 / e.Time));
                font.Write(4, 16, Player.ToString());
                font.Write(4, 30, "Time:" + String.Format("{0:0.#}", world.getWorldTime()));
                font.Write(4, 44, world.worldRender.ToString());
                
                //Right
                font.Write((Width - (world.getSeed().ToString().Length * 40)), 4, "Seed:" + world.getSeed());
            }

            font.Write((Width / 2), 4, "Alpha Build", new FontPrinter.TextAlign(FontPrinter.HorAlign.Center, FontPrinter.VerAlign.Top));
            #endregion

            base.OnRenderFrame(e);            
            base.SwapBuffers();
        }

        protected override void OnResize(EventArgs e)
        {
            GL.Viewport(ClientSize);
            base.OnResize(e);
        }

        void KeyDown(object sender, KeyboardKeyEventArgs e)
        {
            bool showDebug;

            Settings.Config.GetField("Controls", "ShowDebug", out showDebug);

            switch (e.Key)
            {
                case Key.Escape:
                    Exit(); break;

                case Key.P:
                    if (showDebug)
                    {
                        showDebug = false;
                    }
                    else if(!showDebug)
                    {
                        showDebug = true;
                    }
                    break;
            }

            Settings.Config.SetField("Controls", "ShowDebug", showDebug);
        }

        void MouseButtonDown(object sender, MouseButtonEventArgs e)
        {
            Player.mouseClick(e.Button);
        }

        [STAThread]   
        public static void Main(string[] args)
        {
            GraphicsMode mode = new GraphicsMode(32, 24, 0, 0);

            // Request 60 UpdateFrame events per second and as many RenderFrame events as will fit
            using (MosCraft game = new MosCraft(mode))
            {
                game.Run(60D);
            }
        }

    }
}
